﻿using System;
using System.Collections.Generic;
using System.Text;

namespace CoreRay
{
    /// <summary>
    /// VertexFormat class is used by GeometryBuffer to specify the format of one vertex in the buffer.
    /// </summary>
    public class VertexFormat : IEnumerable<VertexFormatElement>, IEquatable<VertexFormat>
    {
        private List<VertexFormatElement> elements;
        private Dictionary<string, int> elementNameMapping;

        /// <summary>
        /// Initializes a new instance of the <see cref="VertexFormat"/> class.
        /// </summary>
        public VertexFormat()
        {
            this.elements = new List<VertexFormatElement>();
            this.elementNameMapping = new Dictionary<string, int>();
        }

        /// <summary>
        /// Gets the size of the vertex represented by this VertexFormat.
        /// </summary>
        /// <value>The size of the vertex.</value>
        public int VertexSize
        {
            get
            {
                int size = 0;

                foreach(VertexFormatElement element in this.elements)
                {
                    size += element.Size;
                }

                return size;
            }
        }

        /// <summary>
        /// Gets the elements count.
        /// </summary>
        /// <value>The elements count.</value>
        public int ElementsCount
        {
            get
            {
                return this.elements.Count;
            }
        }

        /// <summary>
        /// Gets the <see cref="CoreRay.VertexFormatElement"/> with the specified name.
        /// </summary>
        /// <value>VertexFormat Element.</value>
        public VertexFormatElement this[string name]
        {
            get
            {
                // Check to see if the element exists
                if(!this.elementNameMapping.ContainsKey(name))
                {
                    throw new ArgumentException(string.Format("Element with name '{0}' doesn't exist in the VertexFormat.", name), "name");
                }

                return this.elements[this.elementNameMapping[name]];
            }
        }

        /// <summary>
        /// Gets the <see cref="CoreRay.VertexFormatElement"/> at the specified index.
        /// </summary>
        /// <value>VertexFormat Element.</value>
        public VertexFormatElement this[int index]
        {
            get
            {
                return this.elements[index];
            }
        }

        /// <summary>
        /// Adds an element to the VertexFormat.
        /// </summary>
        /// <param name="name">Name of the element.</param>
        /// <param name="type">Type of the element.</param>
        public void AddElement(string name, Type type)
        {
            this.elements.Add(new VertexFormatElement(name, type));
            this.elementNameMapping.Add(name, this.elements.Count - 1);
        }

        /// <summary>
        /// Determines whether the VertexFormat contains an element with the specified name.
        /// </summary>
        /// <param name="name">Name of the element to check.</param>
        /// <returns>
        /// <c>true</c> if the VertexFormat contains an element with the specified name; otherwise, <c>false</c>.
        /// </returns>
        public bool Contains(string name)
        {
            return this.elementNameMapping.ContainsKey(name);
        }

        public int IndexOf(string name)
        {
            if (this.elementNameMapping.ContainsKey(name))
            {
                return this.elementNameMapping[name];
            }

            return -1;
        }

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
        /// </returns>
        public IEnumerator<VertexFormatElement> GetEnumerator()
        {
            return this.elements.GetEnumerator();
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
        /// </returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        public bool Equals(VertexFormat other)
        {
            for(int i = 0; i < this.elements.Count; i++)
            {
                if(this.elements[i] != other.elements[i])
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.</exception>
        public override bool Equals(object obj)
        {
            VertexFormat vertexFormat = obj as VertexFormat;

            if(vertexFormat != null)
            {
                return this.Equals(vertexFormat);
            }

            return false;
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="value1">The value1.</param>
        /// <param name="value2">The value2.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(VertexFormat value1, VertexFormat value2)
        {
            return value1.Equals(value2);
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="value1">The value1.</param>
        /// <param name="value2">The value2.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(VertexFormat value1, VertexFormat value2)
        {
            return !value1.Equals(value2);
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
    }
}
